﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LoRel;
using Microsoft.VisualStudio.TestTools.UnitTesting;

namespace CoreTest.Table.Join
{
    [TestClass]
    public class TestJoinClauseMatchSet
    {
        private Core PrepareCore()
        {
            Core core = new Core();
            core.DeclareField("Name", typeof(String));
            core.DeclareField("DepName", typeof(String));
            core.DeclareField("DepID", typeof(int));
            core.DeclareField("Month", typeof(String));
            core.DeclareField("Other", typeof(String));

            core.DeclareColumn("Emp", "Name");
            core.DeclareColumn("Emp", "DepID");

            core.DeclareColumn("Dep", "DepID");
            core.DeclareColumn("Dep", "DepName");

            core.DeclareColumn("Mo", "DepID");
            core.DeclareColumn("Mo", "Month");

            core.DeclareColumn("Four", "DepID");
            core.DeclareColumn("Four", "Other");
            return core;
        }

        [TestMethod]
        public void TestMatchSetWithThreeParts() {
            Core core = PrepareCore();

            ColumnReference emp_name_col = core.GetColumn("Emp", "Name");
            ColumnReference emp_depid_col = core.GetColumn("Emp", "DepID");

            ColumnReference dep_depid_col = core.GetColumn("Dep", "DepID");
            ColumnReference dep_depname_col = core.GetColumn("Dep", "DepName");
            
            ColumnReference mo_depid_col = core.GetColumn("Mo", "DepID");
            ColumnReference mo_month_col = core.GetColumn("Mo", "Month");

            Key Sam = new Key(NullReport.AllPopulated, "Sam", 1);
            Key Jim = new Key(NullReport.AllPopulated, "Jim", 1);
            Key Jill = new Key(NullReport.AllPopulated, "Jill", 1);
            Key Tim = new Key(NullReport.AllPopulated, "Tim", 1);

            Key A = new Key(NullReport.AllPopulated, 1, "A");
            Key B = new Key(NullReport.AllPopulated, 1, "B");

            Key Jan = new Key(NullReport.AllPopulated, 1, "Jan");
            Key Feb = new Key(NullReport.AllPopulated, 1, "Feb");
            Key March = new Key(NullReport.AllPopulated, 1, "March");

            Key join_key = new Key( NullReport.AllPopulated, 1 );
            IndexBucket emp = new IndexBucket(join_key);
            IndexBucket dep = new IndexBucket(join_key);
            IndexBucket mo = new IndexBucket(join_key);

            Key person_key = new Key(NullReport.AllNull, null, null);
            Key dept_key = new Key(NullReport.AllNull, null, null);
            Key mo_key = new Key(NullReport.AllNull, null, null);

            CombinedKeySchema schema = new CombinedKeySchema(
                new List<ColumnReference>() { emp_name_col, emp_depid_col, 
                                              dep_depid_col, dep_depname_col,
                                              mo_depid_col, mo_month_col },
                new List<Key>() { person_key, 
                                  dept_key, 
                                  mo_key }
                );

            JoinClauseMatchSet match_set = new JoinClauseMatchSet( join_key, 
                schema,
                new List< IndexBucket >() { emp, dep, mo } );


            List<Key> correct_results = new List<Key>();
            
            List<Key> output_results = new List<Key>();
            match_set.NotifyUniqueKeyAdded += delegate(UniqueKey key) { output_results.Add(key); };

            // begin updates
            emp.EnqueueUpdate(GetAdditionArgs( Sam ));
            dep.EnqueueUpdate(GetAdditionArgs( A ));
            mo.EnqueueUpdate( GetAdditionArgs( Jan ) );

            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "A", 1, "Jan"));

            // wont pass because NullReport
            Assert.IsTrue(correct_results.SequenceEqual(output_results));

            emp.EnqueueUpdate(GetAdditionArgs(Jim));
            correct_results.Add(new Key(NullReport.SomeNull, "Jim", 1, 1, "A", 1, "Jan"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));
            
            dep.EnqueueUpdate(GetAdditionArgs(B));
            correct_results.Add(new Key(NullReport.SomeNull, "Sam", 1, 1, "B", 1, "Jan"));
            correct_results.Add(new Key(NullReport.SomeNull, "Jim", 1, 1, "B", 1, "Jan"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));
            
            mo.EnqueueUpdate(GetAdditionArgs(Feb));
            correct_results.Add(new Key(NullReport.SomeNull, "Sam", 1, 1, "A", 1, "Feb"));
            correct_results.Add(new Key(NullReport.SomeNull, "Jim", 1, 1, "A", 1, "Feb"));
            correct_results.Add(new Key(NullReport.SomeNull, "Sam", 1, 1, "B", 1, "Feb"));
            correct_results.Add(new Key(NullReport.SomeNull, "Jim", 1, 1, "B", 1, "Feb"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));
        }

        [TestMethod]
        public void TestMatchSetWithTwoParts()
        {
            Core core = PrepareCore();

            ColumnReference emp_name_col = core.GetColumn("Emp", "Name");
            ColumnReference emp_depid_col = core.GetColumn("Emp", "DepID");

            ColumnReference dep_depid_col = core.GetColumn("Dep", "DepID");
            ColumnReference dep_depname_col = core.GetColumn("Dep", "DepName");

            ColumnReference mo_depid_col = core.GetColumn("Mo", "DepID");
            ColumnReference mo_month_col = core.GetColumn("Mo", "Month");

            Key Sam = new Key(NullReport.AllPopulated, "Sam", 1);
            Key Jim = new Key(NullReport.AllPopulated, "Jim", 1);
            Key Jill = new Key(NullReport.AllPopulated, "Jill", 1);
            Key Tim = new Key(NullReport.AllPopulated, "Tim", 1);

            Key A = new Key(NullReport.AllPopulated, 1, "A");
            Key B = new Key(NullReport.AllPopulated, 1, "B");
            Key C = new Key(NullReport.AllPopulated, 1, "C");

            Key Jan = new Key(NullReport.AllPopulated, 1, "Jan");
            Key Feb = new Key(NullReport.AllPopulated, 1, "Feb");
            Key March = new Key(NullReport.AllPopulated, 1, "March");

            Key join_key = new Key(NullReport.AllPopulated, 1);
            IndexBucket emp = new IndexBucket(join_key);
            IndexBucket dep = new IndexBucket(join_key);
            IndexBucket mo = new IndexBucket(join_key);

            Key person_key = new Key(NullReport.AllNull, null, null);
            Key dept_key = new Key(NullReport.AllNull, null, null);
            Key mo_key = new Key(NullReport.AllNull, null, null);

            CombinedKeySchema schema = new CombinedKeySchema(
                new List<ColumnReference>() { emp_name_col, emp_depid_col, 
                                              dep_depid_col, dep_depname_col },
                new List<Key>() { person_key, 
                                  dept_key }
                );

            JoinClauseMatchSet match_set = new JoinClauseMatchSet(join_key,
                schema,
                new List<IndexBucket>() { emp, dep });


            List<Key> correct_results = new List<Key>();

            List<Key> output_results = new List<Key>();
            match_set.NotifyUniqueKeyAdded += delegate(UniqueKey key) { output_results.Add(key); };

            // begin updates
            emp.EnqueueUpdate(GetAdditionArgs(Sam));
            dep.EnqueueUpdate(GetAdditionArgs(A));
            mo.EnqueueUpdate(GetAdditionArgs(Jan));

            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "A" ));

            Assert.IsTrue(correct_results.SequenceEqual(output_results));

            emp.EnqueueUpdate(GetAdditionArgs(Jim));
            correct_results.Add(new Key(NullReport.SomeNull, "Jim", 1, 1, "A" ));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));     

            dep.EnqueueUpdate(GetAdditionArgs(B));                            
            correct_results.Add(new Key(NullReport.SomeNull, "Sam", 1, 1, "B" ));
            correct_results.Add(new Key(NullReport.SomeNull, "Jim", 1, 1, "B" ));

            mo.EnqueueUpdate(GetAdditionArgs(C));
            correct_results.Add(new Key(NullReport.SomeNull, "Sam", 1, 1, "C" ));
            correct_results.Add(new Key(NullReport.SomeNull, "Jim", 1, 1, "C" ));

            mo.EnqueueUpdate(GetAdditionArgs(Jill));
            correct_results.Add(new Key(NullReport.SomeNull, "Jill", 1, 1, "A"));
            correct_results.Add(new Key(NullReport.SomeNull, "Jill", 1, 1, "B"));
            correct_results.Add(new Key(NullReport.SomeNull, "Jill", 1, 1, "C"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));
        }

        [TestMethod]
        public void TestMatchSetWithFourParts()
        {
            Core core = PrepareCore();

            ColumnReference emp_name_col = core.GetColumn("Emp", "Name");
            ColumnReference emp_depid_col = core.GetColumn("Emp", "DepID");

            ColumnReference dep_depid_col = core.GetColumn("Dep", "DepID");
            ColumnReference dep_depname_col = core.GetColumn("Dep", "DepName");

            ColumnReference mo_depid_col = core.GetColumn("Mo", "DepID");
            ColumnReference mo_month_col = core.GetColumn("Mo", "Month");

            ColumnReference four_depid_col = core.GetColumn("Four", "DepID");
            ColumnReference four_other_col = core.GetColumn("Four", "Other");

            Key Sam = new Key(NullReport.AllPopulated, "Sam", 1);
            Key Jim = new Key(NullReport.AllPopulated, "Jim", 1);
            Key Jill = new Key(NullReport.AllPopulated, "Jill", 1);
            Key Tim = new Key(NullReport.AllPopulated, "Tim", 1);

            Key A = new Key(NullReport.AllPopulated, 1, "A");
            Key B = new Key(NullReport.AllPopulated, 1, "B");

            Key Jan = new Key(NullReport.AllPopulated, 1, "Jan");
            Key Feb = new Key(NullReport.AllPopulated, 1, "Feb");
            Key March = new Key(NullReport.AllPopulated, 1, "March");

            Key FourA = new Key(NullReport.AllPopulated, 1, "FourA");
            Key FourB = new Key(NullReport.AllPopulated, 1, "FourB");

            Key join_key = new Key(NullReport.AllPopulated, 1);
            IndexBucket emp = new IndexBucket(join_key);
            IndexBucket dep = new IndexBucket(join_key);
            IndexBucket mo = new IndexBucket(join_key);
            IndexBucket four = new IndexBucket(join_key);

            Key person_key = new Key(NullReport.AllNull, null, null);
            Key dept_key = new Key(NullReport.AllNull, null, null);
            Key mo_key = new Key(NullReport.AllNull, null, null);
            Key four_key = new Key(NullReport.AllNull, null, null);

            CombinedKeySchema schema = new CombinedKeySchema(
                new List<ColumnReference>() { emp_name_col, emp_depid_col, 
                                              dep_depid_col, dep_depname_col,
                                              mo_depid_col, mo_month_col,
                                              four_depid_col, four_other_col },
                new List<Key>() { person_key, 
                                  dept_key, 
                                  mo_key, 
                                  four_key }
                );

            JoinClauseMatchSet match_set = new JoinClauseMatchSet(join_key,
                schema,
                new List<IndexBucket>() { emp, dep, mo, four });


            List<Key> correct_results = new List<Key>();

            List<Key> output_results = new List<Key>();
            match_set.NotifyUniqueKeyAdded += delegate(UniqueKey key) { output_results.Add(key); };

            // begin updates
            emp.EnqueueUpdate(GetAdditionArgs(Sam));
            dep.EnqueueUpdate(GetAdditionArgs(A));
            mo.EnqueueUpdate(GetAdditionArgs(Jan));
            four.EnqueueUpdate(GetAdditionArgs(FourA));

            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "A", 1, "Jan", 1, "FourA"));

            Assert.IsTrue(correct_results.SequenceEqual(output_results));

            emp.EnqueueUpdate(GetAdditionArgs(Jim));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "A", 1, "Jan", 1, "FourA"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));

            dep.EnqueueUpdate(GetAdditionArgs(B));
            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "B", 1, "Jan", 1, "FourA"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "B", 1, "Jan", 1, "FourA"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));

            mo.EnqueueUpdate(GetAdditionArgs(Feb));
            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "A", 1, "Feb", 1, "FourA"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "A", 1, "Feb", 1, "FourA"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "B", 1, "Feb", 1, "FourA"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "B", 1, "Feb", 1, "FourA"));
            Assert.IsTrue(correct_results.SequenceEqual(output_results));
            four.EnqueueUpdate(GetAdditionArgs(FourB));

            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "A", 1, "Jan", 1, "FourB"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "A", 1, "Jan", 1, "FourB"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "B", 1, "Jan", 1, "FourB"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "B", 1, "Jan", 1, "FourB"));

            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "A", 1, "Feb", 1, "FourB"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Sam", 1, 1, "B", 1, "Feb", 1, "FourB"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "A", 1, "Feb", 1, "FourB"));
            correct_results.Add(new Key(NullReport.AllPopulated, "Jim", 1, 1, "B", 1, "Feb", 1, "FourB"));

            Assert.IsTrue(correct_results.SequenceEqual(output_results));

        }
        private TableChangeArgs GetAdditionArgs(Key key)
        {
            TableChangeArgs args = new TableChangeArgs(ChangeType.Add, 1, new[] { GetAdditionItem(key) });
            return args;
        }

        private TableChangeItem GetAdditionItem(Key key)
        {
            TableChangeItem item = new TableChangeItem(new TableRow(key, null), null);
            return item;
        }
    }
}
